/*
----------------------------------------------------------------------------------------------------
This source file is part of the Senbotsu Project
Copyright (c) 2007 by Ryan Holcombe

This program is free software; you can redistribute it and/or modify it under the terms of the GNU
Lesser General Public License as published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
----------------------------------------------------------------------------------------------------
*/

#include "goAnimated.h"


// -------------------------------------------------------------------------------------------//
void goAnimated::load(const std::string& file, Ogre::SceneManager *pSceneMgr)
{
	// load the mesh
	goEntity::load(file, pSceneMgr);

	// load the base animations to the mStateMap
	Ogre::AnimationStateSet *animSet = mEntity->getAllAnimationStates();
	Ogre::AnimationStateIterator aIter = animSet->getAnimationStateIterator();

	while (aIter.hasMoreElements())
	{
		IState newState;
		newState.mBaseState = aIter.getNext();
		mStateMap[newState.mBaseState->getAnimationName()] = newState;
	}

	_createAnimationsFromStates();
}
 
// -------------------------------------------------------------------------------------------//
void goAnimated::setAnimation(std::string animation, bool loop)
{
	AnimationMap::iterator iter = mAnimationMap.find(animation);

	if (iter == mAnimationMap.end())
		throw Gamut::Exception("goAnimated::setAnimation", "Unable to find animation: [" + animation + "]");

	if (animation == mCurrAnim)
		return;

	// disable all states
	for (StateMap::iterator it = mStateMap.begin(); it != mStateMap.end(); ++it)
		_disableState(it->first);

	StateList sList = iter->second;
	for (StateList::iterator it = sList.begin(); it != sList.end(); ++it)
		_enableState((*it), loop);
}

// -------------------------------------------------------------------------------------------//
void goAnimated::createAllStatesFromXML(const std::string& file)
{
	Gamut::XMLParser xml;
	xml.load(file);

	if (xml.findSibling("AnimationState"))
	{
		do
		{
			_createState(&xml);
		} while (xml.next("AnimationState"));
	}
	else
	{
		LOG2("No animation states found in " + file + " for GO " + mName);
	}

	_createAnimationsFromStates();
}

// -------------------------------------------------------------------------------------------//
void goAnimated::addAnimationsFromXML(const std::string& file)
{
	Gamut::XMLParser xml;
	xml.load(file);

	std::string name;
	StateList v;

	if (xml.findSibling("Animation"))
	{
		// gather all info before creating the new state
		do
		{
			name = xml.getName();

			xml.stepInto();
			do
			{
				if (xml.getTitle() == "AnimationState")
					v.push_back(xml.getName());
				else
					LOG2("Unknown Animation tag found [" + xml.getTitle() + "]");

			} while (xml.next());

			addAnimation(name, v);
			xml.stepBack();
			
		} while (xml.next("Animation"));
	}
	else
	{
		LOG2("No animations found in " + file + " for GO " + mName);
	}
}

// -------------------------------------------------------------------------------------------//
void goAnimated::addAnimation(const std::string& name, const StateList& list)
{
	if (mAnimationMap.find(name) != mAnimationMap.end())
		throw Gamut::Exception("goAnimated::addAnimation", "Animation [" + name + "] already exists in the reference map");

	mAnimationMap[name] = list;
}

// -------------------------------------------------------------------------------------------//
void goAnimated::_outputBoneList(std::ostream &stream) const
{
	if (mEntity == NULL)
		throw Gamut::Exception("goAnimated::_outputBoneList", "Entity has not been loaded");

	if (!mEntity->hasSkeleton())
		throw Gamut::Exception("goAnimated::_outputBoneList", "Loaded entity does not have a skeleton");

	Ogre::Skeleton::BoneIterator bIter = mEntity->getSkeleton()->getBoneIterator();
	stream << "Bone List for " << mName << std::endl;

	while (bIter.hasMoreElements())
	{
		Ogre::Bone *bone = bIter.getNext();
		Ogre::Vector3 pos = mNode->_getFullTransform() * bone->_getDerivedPosition();

		if (pos.x < 0.01 && pos.x > -0.01)
			pos.x = 0.0;
		if (pos.y < 0.01 && pos.y > -0.01)
			pos.y = 0.0;
		if (pos.z < 0.01 && pos.z > -0.01)
			pos.z = 0.0;

		stream << "Id: " << bone->getHandle() << ", Name: " << bone->getName() << std::endl;
		stream << "Pos: " << pos.x << ", " << pos.y << ", " << pos.z << std::endl << std::endl;
	}

	stream << std::endl;

}

// -------------------------------------------------------------------------------------------//
void goAnimated::_outputAnimationList(std::ostream &stream) const
{
	if (mEntity == NULL)
		throw Gamut::Exception("goAnimated::_outputBoneList", "Animated has not been loaded");

	if (!mEntity->hasSkeleton())
		throw Gamut::Exception("goAnimated::_outputBoneList", "Loaded entity does not have a skeleton");

	Ogre::AnimationStateSet *animSet = mEntity->getAllAnimationStates();
	Ogre::AnimationStateIterator aIter = animSet->getAnimationStateIterator();
	stream << "Animations for " << mName << std::endl;

	while (aIter.hasMoreElements())
	{
		Ogre::AnimationState *state = aIter.getNext();

		stream << "Name: " << state->getAnimationName() << ", Length: " << state->getLength() << "s" << std::endl;
	}

	stream << std::endl;
}










// -------------------------------------------------------------------------------------------//
void goAnimated::_createState(Gamut::XMLParser *xml)
{
	// verify we are at an AnimationState
	if (xml->getTitle() != "AnimationState")
		throw Gamut::Exception("goAnimated::_createState", "Not at an AnimationState in xml");

	std::string name;
	std::string copyFrom;
	int boneInheritChildren = 0;
	IState newState;

	// create a new state
	name = xml->getName();

	xml->stepInto();
	do
	{
		if (xml->getTitle() == "StateCopyFrom")
			copyFrom = xml->getValueAsString();
		else if (xml->getTitle() == "BoneMaskDefault")
			newState.mDefaultBoneWeight = xml->getValueAsFloat();
		else if (xml->getTitle() == "BoneInheritChildren")
			boneInheritChildren = xml->getValueAsInt();
		else if (xml->getTitle() == "BoneMask")
		{
			std::string boneName = xml->getName();
			if (!mEntity->getSkeleton()->hasBone(boneName))
				throw Gamut::Exception("goAnimated::_createState", "Bone [" + boneName + "] not found in skeleton for [" + mEntity->getName() + "]");

			Ogre::Bone *bone = mEntity->getSkeleton()->getBone(boneName);
			newState.setBoneMask(bone, xml->getValueAsFloat(), boneInheritChildren);
		}
		else
			LOG2("Unknown AnimationState tag found [" + xml->getTitle() + "]");

	} while (xml->next());


	// verify information from xml file
	if (copyFrom.size() == 0)
		throw Gamut::Exception("goAnimated::_createState", "An StateCopyFrom animation must be supplied (<StateCopyFrom value=\"Attack1\" type=\"string\" />)");

	try {
		newState.mBaseState = mEntity->getAnimationState(copyFrom);
	} catch (Ogre::Exception &e) {
		throw Gamut::Exception("goAnimated::_createState", "Unable to find state [" + copyFrom + "[ for entity [" + mEntity->getName() + "]");
	}

	// insert the new state into the map
	if (mStateMap.find(name) == mStateMap.end())
		mStateMap[name] = newState;

	xml->stepBack();
}

// -------------------------------------------------------------------------------------------//
void goAnimated::_createAnimationsFromStates()
{
	for (StateMap::const_iterator iter = mStateMap.begin(); iter != mStateMap.end(); ++iter)
	{
		StateList newAnim;
		newAnim.push_back(iter->first);
		if (mAnimationMap.find(iter->first) == mAnimationMap.end())
			mAnimationMap[iter->first] = newAnim;
	}
}

// -------------------------------------------------------------------------------------------//
void goAnimated::_enableState(const std::string& stateName, bool loop)
{
	StateMap::iterator iter = mStateMap.find(stateName);
	if (iter == mStateMap.end())
		throw("goAnimated::_enableState", "Animation state [" + stateName + "] not found");

	IState state = iter->second;
	
	if (state.mBoneMaskMap.size() > 0)
	{
		state.mBaseState->createBlendMask(mEntity->getSkeleton()->getNumBones(), state.mDefaultBoneWeight);

		for (BoneMaskMap::iterator it = state.mBoneMaskMap.begin(); it != state.mBoneMaskMap.end(); ++it)
			state.mBaseState->setBlendMaskEntry(it->first, it->second);
	}

	state.mBaseState->setEnabled(1);
	state.mBaseState->setLoop(loop);
}

// -------------------------------------------------------------------------------------------//
void goAnimated::_disableState(const std::string& state)
{
	StateMap::iterator iter = mStateMap.find(state);
	if (iter == mStateMap.end())
		throw("goAnimated::_disableState", "Animation state [" + state + "] not found");
	Ogre::AnimationState *animState = iter->second.mBaseState;
	animState->setEnabled(0);
	animState->setTimePosition(0);

	if (animState->hasBlendMask())
		animState->destroyBlendMask();
}
